﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace DawnOfConquest
{
    class TacMap
    {
        private int width = 30;
        private Tile[,] map;
        private List<Tile> validMoves;
        private List<Tile> validTargets;

        //terrain
        private Texture2D woods;
        private Texture2D woods2;
        private Texture2D mountains;
        private Texture2D river;

        public TacMap(Tile[,] map, ContentManager content)
        {
            this.map = map;
            validMoves = new List<Tile>();
            woods = content.Load<Texture2D>("TerrainSprites/tree 2");
            woods2 = content.Load<Texture2D>("TerrainSprites/tree 1");
            mountains = content.Load<Texture2D>("TerrainSprites/terrain_mountains");
            river = content.Load<Texture2D>("TerrainSprites/river");
            //TODO load all unit sprites and terrain sprites and effects sprites for drawing
        }

        public void addTile(Tile tile, int xCoor, int yCoor)
        {
            // places tile in the given coordinates
            map[xCoor,yCoor] = tile;
        }

        public List<Tile> checkMeleeRange(Tile origin)          //checks neighbors of tiles for targets
        {
            List<Tile> targets = new List<Tile>();
            targets = getNeighbors(origin, targets);
            foreach(Tile t in targets)
                if(t.isEmpty())
                    targets.Remove(t);
            if(targets.Count == 0)
                return null;
            else
                return targets;
        }

        public List<Tile> getNeighbors(Tile tile, List<Tile> neighbors)
        {
            //returns the neighbors of the given tile and adds them to existing list
            //at most, will return 4 tiles
            int xCoord = tile.getXCoord();
            int yCoord = tile.getYCoord();

            //MAYBE TODO, getNeighbors and find moves back to working order

            //prevent array index out of bounds and check for existence of tile in list
            if (xCoord > 0 )
            {
                neighbors.Add(map[xCoord - 1,yCoord]);
            }
            if (xCoord < width - 1 )
            {
                neighbors.Add(map[xCoord + 1,yCoord]);
            }
            if (yCoord > 0 )
            {
                neighbors.Add(map[xCoord,yCoord - 1]);
            }
            if (yCoord < width - 1)
            {
                neighbors.Add(map[xCoord,yCoord + 1]);
            }
            return neighbors;
        }

        public bool isNeighbor(Tile tile1, Tile tile2)
        {
            //returns true if the given tiles are neighbors
            //two tiles are neighbors if either their xCoords or yCoords are one apart (but not both)
            int xCoord1 = tile1.getXCoord();
            int yCoord1 = tile1.getYCoord();
            int xCoord2 = tile2.getXCoord();
            int yCoord2 = tile2.getYCoord();
            if (Math.Abs(xCoord1 - xCoord2) == 1 && (yCoord1 - yCoord2 == 0))
            {
                return true;
            }
            else if (Math.Abs(yCoord1 - yCoord2) == 1 && (xCoord1 - xCoord2 == 0))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void draw(SpriteBatch spriteBatch, ContentManager content)
        {
            //Draws the map background then goes through every tile and calls tile.draw(X,Y) depending on the tile’s location in the array
            //TODO modify so that for each tile's terrain and unit are drawn in this class by
            //finding what is in the tile and draw it using the loaded sprites in this class
            Vector2 location;
            Tile current;

           // Random r = new Random();
            for (int i = 0; i < map.GetLength(0); i++)
                for (int j = 0; j < map.GetLength(1); j++)
                {
                    location = new Vector2(i * 30, j * 30);
                    current = map[i, j];
                    // if tile.terrain = whatever draw whatever at position i j
                    //if (current.getTerrain().Equals("Woods")&& r.Next(6) % 6 ==0)
                    //    spriteBatch.Draw(woods2, location, Color.White);
                     if (current.getTerrain().Equals("Woods"))
                        spriteBatch.Draw(woods, location, Color.White);
                    else if (current.getTerrain().Equals("Mountain"))
                        spriteBatch.Draw(mountains, location, Color.White);
                    else if (current.getTerrain().Equals("Water"))
                        spriteBatch.Draw(river, location, Color.White);

                    current.draw(i * 30, j * 30, spriteBatch, content);

                    //draw unit class at location using peice draw method which takes vector and spritebatch
                    //if validMoves contains tile then put green box on it

                }
        }



        //TODO similar to valid moves, find a list of all tiles within 
        //Reinitializes the list of moves and starts the recursive call
        public List<Tile> findValidMoves(Piece piece)
        {
            //List<Tile> validMoves = new List<Tile>();
            validMoves = new List<Tile>();
            findMoves(map[piece.getXPosition(), piece.getYPosition()], piece.getAP(), piece);
            return validMoves;
        }

        public List<Tile> findValidTargets(Piece piece)
        {
            //returns a list of Tiles that contain units whose faction != to the passed faction
            validTargets = new List<Tile>();
            findTargets(map[piece.getXPosition(), piece.getYPosition()], piece.getRange(),  piece);
            return validTargets;
        }

        private void findMoves(Tile location, int ap, Piece piece)
        {
            int remainingPoints = ap;
            List<Tile> neighbors = getNeighbors(location, new List<Tile>());

            for (int i = 0; i < neighbors.Count; i++)
            {
                Tile tile = neighbors[i];
                int cost;
                if (tile.getTerrain().Equals("Plain"))
                {
                    cost = 1;
                }
                else if (tile.getTerrain().Equals("Woods"))
                {
                    cost = 2;
                }
                else if (tile.getTerrain().Equals("Mountain"))
                {
                    cost = 4;
                }
                else
                {
                    cost = 999;
                }
                //If there is sufficient ap
                if (remainingPoints >= cost)
                {
                    //If the tile is empty it is added to the list and another call made
                    if (neighbors[i].isEmpty())
                    {
                        //if validMoves does not already hold the tile
                        if (!validMoves.Contains(neighbors[i]))
                        {
                            validMoves.Add(neighbors[i]);
                        }
                        findMoves(neighbors[i], remainingPoints - cost, piece);
                    }
                    else
                    {
                        //if the tile is occupied BUT is occupied by a friendly another call is made but the tile is not added, the tile is not a valid move 
                        //but the search still "paths" through it
                        if (neighbors[i].getResident().getFaction().Equals(piece.getFaction()))
                        {
                            findMoves(neighbors[i], remainingPoints - cost, piece);
                        }
                    }
                }
            }
        }

        public void findTargets(Tile location, int range, Piece piece)
        {
            int remainingRange = range;
            List<Tile> neighbors = getNeighbors(location, new List<Tile>());

            for (int i = 0; i < neighbors.Count; i++)
            {
                //If there is sufficient ap and validMoves does not already hold that tile
                if (remainingRange >= 1 && !validTargets.Contains(neighbors[i]))
                {
                    //If the tile is empty it is not added to the list and another call made
                    if (neighbors[i].isEmpty())
                    {
                        //validTargets.Add(neighbors[i]);
                        findTargets(neighbors[i], remainingRange - 1, piece);
                    }
                    else
                    {
                        //if the tile is occupied BUT is occupied by a friendly another call is made but the tile is not added, the tile is not a valid target 
                        //but the search still "paths" through it
                        if (neighbors[i].getResident().getFaction().Equals(piece.getFaction()))
                        {
                            findTargets(neighbors[i], remainingRange - 1, piece);
                        }
                        //if the tile is occupied by an enemy faction, add it to validTargets
                        else
                        {
                            validTargets.Add(neighbors[i]);
                            findTargets(neighbors[i], remainingRange - 1, piece);
                        }
                    }
                }
            }
        }

        //updates position on the piece and tile objects
        public String movePiece(Tile origin, Tile destination, Piece piece)
        {
            if (validMoves.Contains(destination))
            {
                origin.removePiece();
                destination.enterPiece(piece);
                
                // promptAbility();
                piece.move();
                validMoves = null;
                return piece.getName() + " has moved";

            }
            else
                return "invalid location";
        }
        //Returns a list of tiles containing a valid target in range of the given ability
        //That list is stored on the ability object and is cleared with each new (non-recursive) call to this method
        
        public List<Tile> checkRange(Tile origin, Ability ability, int range, Piece user)
        {
            //Clears the list if this is the initial call because the parameter "range" will only equal ability.range if this is the first call
            if (ability.getRange() == range)
            {
                validTargets = new List<Tile>();
            }
            List<Tile> frontier = getNeighbors(origin, new List<Tile>());
            if (!origin.isEmpty())
            {
                //If ability targets enemies & the tile contains a unit from the other faction & if the tile is not already in the list THEN the tile is added
                if (ability.getTargetType().Equals("Enemy") && !(origin.getResident().getFaction().Equals(user.getFaction())) && !validTargets.Contains(origin))
                {
                    validTargets.Add(origin);
                }
                //If ability targets friendlies & the tile contains a unit from the same faction & if the tile is not already in the list THEN the tile is added
                else if (ability.getTargetType().Equals("Friendly") && (origin.getResident().getFaction().Equals(user.getFaction())) && !validTargets.Contains(origin))
                {
                    validTargets.Add(origin);
                }
            }
            //decrement range for the next round of checks
            int nRange = range - 1;
            if (nRange >= 0)
            {
                for (int i = 0; i < frontier.Count(); i++)
                {
                    checkRange(frontier[i], ability, nRange, user);
                }
            }
            return validTargets;
        }

        public void randomMap()
        {
            //constructs a randomly generated map
            //start by setting every tile to be plains
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    map[i, j] = new Tile("Plain", i, j);
                }
            }
            //determine how many types of terrain will be on the map
            Random _r = new Random();

            //woods
            int ir = 5 + _r.Next(16);
            for (int i = 0; i < ir; i++)
            {
                int x = _r.Next(30);
                int y = _r.Next(30);

                int jr = _r.Next(12);
                int kr = _r.Next(12);
                for (int j = 0; j < jr; j++)
                {
                    //int kr = _r.Next(12);
                    for (int k = 0; k < kr; k++)
                    {
                        if (_r.Next(8) != 0 && x+j < 30 && y+k <30)
                        {
                            map[x + j, y + k] = new Tile("Woods", x + j, y + k);
                        }
                    }
                }
            }

            //Mountains
            ir = 3 + _r.Next(5);
            for (int i = 0; i < ir; i++)
            {
                int x = _r.Next(30);
                int y = _r.Next(30);

                int jr = _r.Next(7);
                int kr = _r.Next(7);
                for (int j = 0; j < jr; j++)
                {
                    //int kr = _r.Next(7);
                    for (int k = 0; k < kr; k++)
                    {
                        if (_r.Next(10) != 0 && x + j < 30 && y + k < 30)
                        {
                            map[x + j, y + k] = new Tile("Mountain", x + j, y + k);
                        }
                    }
                }
            }

            //water
            ir = 1 + _r.Next(2);
            for (int i = 0; i < ir; i++)
            {
                int x = _r.Next(11) + 7;
                int y = _r.Next(11) + 7;

                int jr = _r.Next(9);
                int kr = _r.Next(9);
                for (int j = 0; j < jr; j++)
                {
                    //int kr = _r.Next(9);
                    for (int k = 0; k < kr; k++)
                    {
                        if (_r.Next(20) != 0 && x + j < 30 && y + k < 30)
                        {
                            map[x + j, y + k] = new Tile("Water", x + j, y + k);
                        }
                    }
                }
            }

            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    map[i, j] = new Tile("Plain", i, j);
                }
            }

            for (int i = 24; i < 30; i++)
            {
                for (int j = 24; j < 30; j++)
                {
                    map[i, j] = new Tile("Plain", i, j);
                }
            }
        }

        public Tile getTile(int x, int y)
        {
           // if (x > 0 && y > 0)
                return map[x, y];
           // else
              //  return null;
        }

        public List<Tile> getValidMoves()
        { return validMoves; }

    }
}